package be.android.forap;
import java.io.ByteArrayOutputStream;

import be.android.forap.crypto.AesOcb;
import be.android.forap.crypto.KeyUtil;
import be.android.forap.gui.AccountFailActivity;
import be.android.forap.gui.MainActivity;
import be.android.forap.network.DropboxActivity;
import be.android.forap.network.DropboxUtil;
import be.android.forap.sync.AccountAuthenticatorService;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountManager;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Activity which displays login screen to the user.
 */
public class ForApAuthenticator extends AccountAuthenticatorActivity{

	public static final String PHNB = "0479409147";

	/** The tag used to log to adb console. */
	private static final String TAG = "ForApAuthenticator";

	private static final int MIN_PASS_LENGTH = 1;

	private static final int DROPBOX_LINK = 0;

	/** Keep track of the login task so can cancel it if requested */
	private UserLoginTask mAuthTask = null;
	private UserSyncTask mSyncTask = null;
	/** Keep track of the progress dialog so we can dismiss it */
	private ProgressDialog mProgressDialog = null;

	private String mUsername;
	private String mPassword;
	private String mPasswordPrime;

	private EditText mUsernameEdit;	
	private EditText mPasswordEdit;
	private EditText mPasswordPrimeEdit;

	private Button mLoginCreateButton;
	private Button mDeleteSyncButton;

	private TextView mDropboxLinkStatusTextview;

	private ByteArrayOutputStream secretOut; 
	private ByteArrayOutputStream publicOut;

	private boolean mHasLocalAccount;

	private ForAp app;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//initialize vars
		mHasLocalAccount = AccountAuthenticatorService.hasForApAccount(this);
		app = ForAp.getInstance();		
		String pref_user = app.getUser();
		String pref_pass = app.getPassword();


		if(!mHasLocalAccount) {
			pref_pass = "";
			app.logout();
		}
		if (app.isLoggedIn() && app.isLinkedToDropbox()){//!TextUtils.isEmpty(pref_user) && !TextUtils.isEmpty(pref_pass) && app.isLinkedToDropbox()) {
			Intent main = new Intent(this, MainActivity.class);
			startActivity(main);
			finish();
			return;
		}
		if(!app.isLinkedToDropbox()){
			Intent db = new Intent(mContext, DropboxActivity.class);
			startActivityForResult(db,DROPBOX_LINK);			
		}

		//Layout		
		setContentView(R.layout.login);
		mUsernameEdit = (EditText) findViewById(R.id.log_editText_username);
		mPasswordEdit = (EditText) findViewById(R.id.log_editText_password);
		mPasswordPrimeEdit = (EditText) findViewById(R.id.log_editText_password_prime);
		if (!TextUtils.isEmpty(pref_user))
			mUsernameEdit.setText(pref_user);
		mLoginCreateButton = (Button) findViewById(R.id.log_button_logincreate);
		mDeleteSyncButton = (Button) findViewById(R.id.log_button_deletesync);
		updateButtonLabel();
		mDropboxLinkStatusTextview = (TextView) findViewById(R.id.log_textview_dropboxstatus);
		updateDropboxLinkLabel(app.isLinkedToDropbox());
	}

	private void updateButtonLabel() {
		if(mHasLocalAccount){
			mLoginCreateButton.setText("Login");
			mDeleteSyncButton.setText("Remove Account");
		}else{
			mLoginCreateButton.setText("Create Account");
			mDeleteSyncButton.setText("Synchronize Account");
		}
	}

	private void updateDropboxLinkLabel(boolean link){
		if(link){
			mDropboxLinkStatusTextview.setText(R.string.dropbox_linked);
			mLoginCreateButton.setEnabled(true);
			mDeleteSyncButton.setEnabled(true);
		}else{
			mDropboxLinkStatusTextview.setText(R.string.dropbox_unlinked);
			mLoginCreateButton.setEnabled(false);
			mDeleteSyncButton.setEnabled(false);
		}		
	}

	public void onClick(View v){
		switch (v.getId()) {
		case R.id.log_button_logincreate:
			if(mHasLocalAccount)
				login();
			else
				create();
			break;		
		case R.id.log_button_deletesync:
			if(mHasLocalAccount)
				delete();
			else
				sync();
			break;
		case R.id.log_textview_dropboxstatus:
			dropboxPopup();
			break;
		}
	}	

	private void login() {		
		if (loginTest() ) {
			ForAp.getInstance().login();
			Toast.makeText(this, "Login successfull", Toast.LENGTH_SHORT).show();
			Intent main = new Intent(this, MainActivity.class);
			startActivity(main);
			finish();						
		}				
	}

	private void delete(){
		if (loginTest() ) {
			Toast.makeText(this, "Credentials verified", Toast.LENGTH_SHORT).show();
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage("Remove current ForAp account?")
			.setCancelable(false)
			.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int id) {
					ForAp.getInstance().removeAccount();
					AccountAuthenticatorService.removeForApAccount(getApplicationContext());
					mHasLocalAccount = false;
					updateButtonLabel();
					mUsernameEdit.setText("");
					mPasswordEdit.setText("");
					mPasswordPrimeEdit.setText("");
				}
			})
			.setNegativeButton("No", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int id) {
					dialog.cancel();
				}
			});
			AlertDialog alert = builder.create();
			alert.show();
		}
	}

	private boolean loginTest(){
		mUsername = mUsernameEdit.getText().toString().trim();
		mPassword = mPasswordEdit.getText().toString().trim();
		mPasswordPrime = mPasswordPrimeEdit.getText().toString().trim();
		ForAp app = ForAp.getInstance();
		if(!(mPassword.equals(mPasswordPrime))){
			Toast.makeText(this, "Passwords don't match", Toast.LENGTH_SHORT).show();
			return false;
		}
		if (!(mUsername.equals(app.getUser()))){
			Toast.makeText(this, "Incorrect username"+app.getUser(), Toast.LENGTH_SHORT).show();
			return false;
		}
		if (!( mPassword.equals(app.getPassword()))){
			Toast.makeText(this, "Incorrect password"+app.getPassword(), Toast.LENGTH_SHORT).show();
			return false;
		}	
		return true;
	}

	private void create() {
		if(AccountAuthenticatorService.hasForApAccount(mContext)) {
			Intent i = new Intent(mContext, AccountFailActivity.class);
			startActivity(i);
			return;
		} else{
			mUsername = mUsernameEdit.getText().toString().trim();
			mPassword = mPasswordEdit.getText().toString().trim();
			mPasswordPrime = mPasswordPrimeEdit.getText().toString().trim();
			if (hasValidUserName(mUsername) && hasValidPassword(mPassword, mPasswordPrime)) {
				showProgress();
				mAuthTask = new UserLoginTask();
				mAuthTask.execute();			
			}
		}		
	}

	private void sync() {
		mUsername = mUsernameEdit.getText().toString().trim();
		mPassword = mPasswordEdit.getText().toString().trim();
		mPasswordPrime = mPasswordPrimeEdit.getText().toString().trim();
		if(hasValidUserName(mUsername) && hasValidPassword(mPassword, mPasswordPrime)) {
			mSyncTask = new UserSyncTask(this, mUsername, mPassword);
			mSyncTask.execute();
		}
	}



	private void dropboxPopup(){		
		final Boolean link = ForAp.getInstance().isLinkedToDropbox();
		Toast.makeText(this, "status" + link, Toast.LENGTH_LONG).show();
		String message = "Link dropbox?";
		if(link){
			message = "Unlink dropbox?";
		}
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(message)
		.setCancelable(false)
		.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				if(link){
					DropboxUtil.logOutFormDropBox();
					updateDropboxLinkLabel(false);
				}else{
					Intent db = new Intent(mContext, DropboxActivity.class);
					startActivityForResult(db,DROPBOX_LINK);
				}

			}
		})
		.setNegativeButton("No", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				dialog.cancel();
			}
		});
		AlertDialog alert = builder.create();
		alert.show();
	}

	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == DROPBOX_LINK) {
			if (resultCode == RESULT_OK) {
				updateDropboxLinkLabel(true);
				app = ForAp.getInstance();
				if(app.isLoggedIn()){
					Intent main = new Intent(this, MainActivity.class);
					startActivity(main);
					finish();
					return;
				}
			}
			if (resultCode == RESULT_CANCELED) {
				this.finish();
			}
		}
	}

	private boolean hasValidUserName(String email){
		if(email.matches("^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$")){
			return true;
		}else{
			Toast.makeText(getApplicationContext(), "Please use a valid e-mail address", Toast.LENGTH_SHORT).show();;
			return false;
		}
	}

	private boolean hasValidPassword(String p,String r){
		if(!r.equals(p)){
			Toast.makeText(getApplicationContext(), "Passwords do not match", Toast.LENGTH_SHORT).show();
			return false;
		}
		if(p.length()< MIN_PASS_LENGTH){
			Toast.makeText(getApplicationContext(), "Password needs to be at least "+MIN_PASS_LENGTH+" long", Toast.LENGTH_SHORT).show();;
			return false;
		}

		/*if(p.matches("((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{"+MIN_PASS_LENGTH+",})")){
			Toast.makeText(getApplicationContext(), "Password needs to contain one lowercase, one uppercase and one numeric character", Toast.LENGTH_SHORT).show();;
			return false;
		}*/	
		return true;

	}

	private void showProgress(){
		mLoginCreateButton.setEnabled(false);
		mDeleteSyncButton.setEnabled(false);
		mProgressDialog = ProgressDialog.show(this, "Please wait...", "Generating encryption keys for new account", true, false);
		mProgressDialog.setCancelable(false);
		mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				Log.i(TAG, "user canncelling authentification");
				if(mAuthTask !=null){
					mAuthTask.cancel(true);
				}
			}
		});
	}



	private void hideProgress() {
		mLoginCreateButton.setEnabled(true);
		mDeleteSyncButton.setEnabled(true);
		if(mProgressDialog != null){
			mProgressDialog.dismiss();
			mProgressDialog = null;
		}

	}


	private Context mContext = this;
	/**
	 * In a task because it can take a while, and Android has a tendency to
	 * panic and show the force quit/wait dialog quickly. And this blocks.
	 */
	private class UserLoginTask extends AsyncTask<Void, Void, Boolean> {

		@Override
		public Boolean doInBackground(Void... params) {
			try {

				//Create keypair for user
				secretOut = new ByteArrayOutputStream(); 
				publicOut = new ByteArrayOutputStream();
				//Choose between keypairs
				//KeyUtil.generateElGamalDSAKeyPair(secretOut, publicOut, mUsername, mPassword.toCharArray(), true);
				KeyUtil.generateRSAKeypair(secretOut, publicOut, mUsername, mPassword.toCharArray(), true);
				//TODO NONCE
				//Upload to dropbox using aes-ocb
				AesOcb engine = new AesOcb();
				byte[] key = KeyUtil.generatePBEKey(mPassword.toCharArray());
				engine.init(key, 128);
				byte[] nonce = new byte[16];
				byte[] tag = new byte[16];
				/*for (int t = 0; t < 15; t++) {
		            nonce[t] = (byte)0;
		        }
		        nonce[15] = (byte)1;*/
				nonce = app.getNonce();

				byte[] encrypted = engine.encrypt(secretOut.toByteArray(), secretOut.toByteArray().length, nonce, tag);
				/*ByteArrayOutputStream upload = new ByteArrayOutputStream();
				final byte[] split = "----".getBytes("UTF-8");

				/*upload.write(Nonce);
				upload.write(split);
				upload.write(Tag);
				upload.write(split);
				upload.write(encrypted);

				upload.close();*/

				DropboxUtil.uploadFile(publicOut.toByteArray(), Constants.PUBLICKEY_FILE_NAME);
				DropboxUtil.uploadFile(encrypted, Constants.CONFIG_FILE_NAME);
				DropboxUtil.uploadFile(nonce, Constants.NONCE_FILE);
				DropboxUtil.uploadFile(tag, Constants.TAG_FILE);
				byte[] cyphertext = encrypted;




				byte[] decryption = engine.decrypt(cyphertext, cyphertext.length, nonce, tag);

				if(secretOut.toString().equals(new String (decryption))){
					Log.e(TAG,"gg");//Toast.makeText(mContext, "GG", Toast.LENGTH_LONG).show();
				} else {
					Log.e(TAG,"loose");
				}


				return true;
			} catch (Exception e) {

				e.printStackTrace();
				return false;
			}
		}

		@Override
		public void onPostExecute(Boolean result) {
			hideProgress();
			onAuthenticationResult(result);	
		}

		@Override
		public void onCancelled(){
			OnAuthenticationCancelled();
		}
	}

	public void onAuthenticationResult(Boolean success) {
		Log.i(TAG, "onAuthenticationResult(" + success + ")");
		// Our task is complete, so clear it out
		mAuthTask = null;
		if (success) {
			ForAp.getInstance().createAccount(mUsername, mPassword, publicOut.toString(), secretOut.toString());
			final Account account = new Account(mUsername, Constants.ACCOUNT_TYPE);
			AccountManager am = AccountManager.get(mContext);
			// Set contacts sync for this account.
			ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
			ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);

			am.addAccountExplicitly(account, mPassword, null);
			
			Log.i(TAG, "finishLogin()");

			if (getIntent().getAction() != null && getIntent().getAction().equals("be.android.forap.sync.LOGIN_ONLY")) {	
				Bundle extras = getIntent().getExtras();
				if (extras != null) {
					finish();
				}
			}else{
				Intent i = new Intent(this, MainActivity.class);
				startActivity(i);
				finish();
			}

		} else {
			Log.e(TAG, "onAuthenticationResult: failed to authenticate");
			Toast.makeText(this, "ERROR authentification process", Toast.LENGTH_SHORT).show();
		}
	}

	public void OnAuthenticationCancelled() {
		mAuthTask = null;
		hideProgress();
	}

	public void onBackPressed() {
		this.finish();
	}
}